package com.easystore.orm.dao
{
import com.easystore.orm.entity.Entity;
import com.easystore.orm.metadata.ColumnMapping;
import com.easystore.orm.metadata.MetadataManager;
import com.easystore.orm.metadata.SQLMetadata;
import com.easystore.orm.pool.ConnectionPool;
import com.easystore.orm.utilities.CheckUtil;

import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.events.Event;
import flash.events.SQLErrorEvent;
import flash.events.SQLEvent;
import flash.net.Responder;

import mx.collections.ArrayCollection;

public class DAO
{
	public function DAO(internalClass : Internal) {
		CheckUtil.checkNotNull(internalClass,"internalClass", "DAO.<constructor>" );
	}
	
	
	//[Transactionnal=true]
	public static function findAllSynchronously(objectClass : Class) : ArrayCollection {
		var syncSqlConnection : SQLConnection = ConnectionPool.getInstance().acquireSyncConnection();
		syncSqlConnection.begin();
		var resultQuery : Object = null;
		var statement : SQLStatement = new SQLStatement();
		statement.sqlConnection = syncSqlConnection;
		statement.text = MetadataManager.getInstance().getSQLMetadata(objectClass).findAllQuery;
		statement.execute();
		resultQuery  = statement.getResult().data;
		var result : ArrayCollection =  toArrayCollection( resultQuery as Array, objectClass );
		releaseConnection(syncSqlConnection);
		return result;
	}
	
	public static function findAllAsynchronously(objectClass : Class, responder : SQLResponder) : void {
		var asyncConnection : SQLConnection = ConnectionPool.getInstance().acquireAsyncConnection(responder.fault);
		//asyncConnection.begin();
		var resultQuery : Object = null;
		var statement : SQLStatement = new SQLStatement();
		statement.sqlConnection = asyncConnection;
		statement.text = MetadataManager.getInstance().getSQLMetadata(objectClass).findAllQuery;
		var internalResult : Function = function (event : Event) : void {
			responder.result( toArrayCollection(statement.getResult().data, objectClass) );
		} ;		
		statement.addEventListener(SQLEvent.RESULT, internalResult );
		statement.addEventListener(SQLErrorEvent.ERROR , responder.fault);
		statement.execute();
	}
	
	/* public function find(clazz : Class, index : *) : Entity {
		return toObject( executeSelectQuery(getSQLMetadata(clazz).findQuery,index), clazz);
	} */
	
	/* public function deleteItem(item : Entity) : void {
		executeDeleteQuery( getSQLMetadata(item).deleteQuery , item  );
	}
	
	public function saveItem(item : Entity) : void  {
		executeUpdateQuery( getSQLMetadata(item).insertQuery ,item );
	}
	
	public function updateItem(item : Entity) : void  {
		executeUpdateQuery( getSQLMetadata(item).updateQuery,item );
	} 
	
	public function deleteAll(items : ArrayCollection, clazz : Class) : void {
		executeDeleteQuery( "DELETE FROM " + getSQLMetadata(clazz).tableName , items );
	} */
	
	/* public function saveAll(items : ArrayCollection) : void {
	}
	
	public function updateAll(items : ArrayCollection) : void {
		
	} */
	
	/* private function executeDeleteQuery( queryString : String , ...arg) : Object {
		return null;
	}
	private function executeUpdateQuery( queryString : String , ...arg) : Object {
		return null;
	} */

	
	private static function releaseConnection(connection : SQLConnection, faultCallback : Function = null) : void  {
		ConnectionPool.getInstance().releaseConnection(connection, faultCallback);	
	}
	
	private static function executeSelectQuery(sqlConnection : SQLConnection,  queryString : String , responder : Responder = null) : Object {
		var result : Object = null;
		var statement : SQLStatement = new SQLStatement();
		statement.sqlConnection = sqlConnection;
		statement.text = queryString;
		if ( ! sqlConnection.synchronous && responder ) {
			statement.execute(-1, responder);
		} else {
			statement.execute();
			result = statement.getResult().data;
		}
		return result;
	}
	
	private static function getSQLMetadata(classOrInstance : Object) : SQLMetadata {
		return MetadataManager.getInstance().getSQLMetadata(classOrInstance);
	}
	
	private static function toArrayCollection ( anArray : Array , clazz : Class) : ArrayCollection {
		CheckUtil.checkNotNull(clazz , "clazz");
		if ( anArray == null ) return null;
		var collection : ArrayCollection = new ArrayCollection();
		var len : int = anArray.length;
		for (var i : int = 0; i< len ; i++)
		{
			collection.addItem(toObject(anArray[i],clazz));
		}
		return collection;	
	}
	
	private static function toObject ( anObject : Object , clazz : Class) : Entity {
		CheckUtil.checkNotNull(anObject , "anObject");
		CheckUtil.checkNotNull(clazz , "clazz");
		var newInstance : Entity = Entity(new clazz());
		var columnMapping : ColumnMapping  = null;
		var fields : ArrayCollection = getSQLMetadata(clazz).columnMappings;
		var len : int = fields.length;			
		for (var i:int = 0; i<len ; i++)
		{
			columnMapping  = ColumnMapping(fields.getItemAt(i));
			newInstance[columnMapping.propertyName] = anObject[columnMapping.columnName];	
		} 
		
		fields = getSQLMetadata(clazz).indexMapping.columnMappings;
		len = fields.length;			
		for (i = 0 ; i<len ; i++)
		{
			columnMapping  = ColumnMapping(fields.getItemAt(i));
			newInstance[columnMapping.propertyName] = anObject[columnMapping.columnName];	
		} 
		
		return newInstance;	
	}
}
}
class Internal {}